import datetime

from syscore.exceptions import missingData
from sysobjects.production.process_control import (
    dictOfControlProcesses,
    controlProcess,
    was_running_pid_notok_closed,
    processNotRunning,
)
from syscore.constants import named_object, success
from sysdata.base_data import baseData
from syslogging.logger import *


class controlProcessData(baseData):
    def __init__(self, log=get_logger("controlProcessData")):
        super().__init__(log=log)
        self._control_store = dict()

    def get_dict_of_control_processes(self) -> dictOfControlProcesses:
        list_of_names = self.get_list_of_process_names()
        output_dict = dict(
            [
                (process_name, self.get_control_for_process_name(process_name))
                for process_name in list_of_names
            ]
        )
        output_dict = dictOfControlProcesses(output_dict)

        return output_dict

    def get_list_of_process_names(self) -> list:
        return list(self._control_store.keys())

    def get_control_for_process_name(self, process_name) -> controlProcess:
        try:
            control = self._get_control_for_process_name_without_default(process_name)
        except missingData:
            return controlProcess()
        else:
            return control

    def _get_control_for_process_name_without_default(
        self, process_name
    ) -> controlProcess:
        try:
            control = self._control_store[process_name]
        except KeyError:
            raise missingData("Process %s not found in control store" % process_name)
        return control

    def _update_control_for_process_name(self, process_name, new_control_object):
        try:
            self._get_control_for_process_name_without_default(process_name)
        except missingData:
            self._add_control_for_process_name(process_name, new_control_object)
        else:
            self._modify_existing_control_for_process_name(
                process_name, new_control_object
            )

    def _add_control_for_process_name(self, process_name, new_control_object):
        self._control_store[process_name] = new_control_object

    def _modify_existing_control_for_process_name(
        self, process_name, new_control_object
    ):
        self._control_store[process_name] = new_control_object

    def delete_control_for_process_name(self, process_name):
        raise NotImplementedError

    def check_if_okay_to_start_process(self, process_name: str) -> named_object:
        """

        :param process_name: str
        :return:  success, or if not okay: process_no_run, process_stop, process_running
        """
        original_process = self.get_control_for_process_name(process_name)
        result = original_process.check_if_okay_to_start_process()

        return result

    def start_process(self, process_name: str) -> named_object:
        """

        :param process_name: str
        :return:  success, or if not okay: process_no_run, process_stop, process_running
        """
        original_process = self.get_control_for_process_name(process_name)
        result = original_process.start_process()
        if result is success:
            self._update_control_for_process_name(process_name, original_process)

        return result

    def check_if_should_pause_process(self, process_name: str) -> bool:
        original_process = self.get_control_for_process_name(process_name)
        result = original_process.check_if_should_pause()

        return result

    def check_if_pid_running_and_if_not_finish_all_processes(self):
        list_of_names = self.get_list_of_process_names()
        list_of_results = [
            self.check_if_pid_running_and_if_not_finish(process_name)
            for process_name in list_of_names
        ]

        return list_of_results

    def check_if_pid_running_and_if_not_finish(self, process_name: str):
        original_process = self.get_control_for_process_name(process_name)
        PID = original_process.process_id
        result = original_process.check_if_pid_running_and_if_not_finish_return_status()

        if result is was_running_pid_notok_closed:
            self.log.critical(
                "Process %s with PID %d appears to have crashed, marking as close: you may want to restart"
                % (process_name, PID)
            )
            self._update_control_for_process_name(process_name, original_process)

    def finish_all_processes(self):
        list_of_names = self.get_list_of_process_names()
        for process_name in list_of_names:
            try:
                self.finish_process(process_name)
            except processNotRunning:
                pass

        return success

    def finish_process(self, process_name: str):
        """

        :param process_name: str
        :return: sucess or failure if can't finish process (maybe already running?)
        """
        original_process = self.get_control_for_process_name(process_name)
        original_process.finish_process()
        self._update_control_for_process_name(process_name, original_process)

    def check_if_process_status_stopped(self, process_name: str) -> bool:
        """

        :param process_name: str
        :return: bool
        """
        original_process = self.get_control_for_process_name(process_name)
        result = original_process.check_if_process_status_stopped()

        return result

    def has_process_finished_in_last_day(self, process_name: str) -> bool:
        """

        :param process_name: str
        :return: bool
        """
        original_process = self.get_control_for_process_name(process_name)
        result = original_process.has_process_finished_in_last_day()

        return result

    def change_status_to_stop(self, process_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.change_status_to_stop()
        self._update_control_for_process_name(process_name, original_process)

    def change_status_to_go(self, process_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.change_status_to_go()
        self._update_control_for_process_name(process_name, original_process)

    def change_status_to_no_run(self, process_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.change_status_to_no_run()
        self._update_control_for_process_name(process_name, original_process)

    def change_status_to_pause(self, process_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.change_status_to_pause()
        self._update_control_for_process_name(process_name, original_process)

    def log_start_run_for_method(self, process_name: str, method_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.log_start_run_for_method(method_name)
        self._update_control_for_process_name(process_name, original_process)

    def log_end_run_for_method(self, process_name: str, method_name: str):
        original_process = self.get_control_for_process_name(process_name)
        original_process.log_end_run_for_method(method_name)
        self._update_control_for_process_name(process_name, original_process)

    def when_method_last_started(
        self, process_name: str, method_name: str
    ) -> datetime.datetime:
        original_process = self.get_control_for_process_name(process_name)
        return original_process.when_method_last_started(method_name)

    def when_method_last_ended(
        self, process_name: str, method_name: str
    ) -> datetime.datetime:
        original_process = self.get_control_for_process_name(process_name)
        return original_process.when_method_last_ended(method_name)

    def method_currently_running(self, process_name: str, method_name: str) -> bool:
        original_process = self.get_control_for_process_name(process_name)
        return original_process.method_currently_running(method_name)
